# -*- coding: utf-8 -*-
import atexit
import sys
import ssl
from pyVim.connect import Disconnect, SmartConnect
from pyVmomi import vim
from pyVim.task import WaitForTask


class vCenterManager():
    def __init__(self, vcenter_ip, vcenter_port, vcenter_user, vcenter_pwd):
        self.vcenter_ip = vcenter_ip
        self.vcenter_port = vcenter_port
        self.vcenter_user = vcenter_user
        self.vcenter_pwd = vcenter_pwd
        self.context = None
        self.si = None
        self.content = None

        # 建立与vSphere的连接
        if hasattr(ssl, '_create_unverified_context'):
            self.context = ssl._create_unverified_context()
        self.si = SmartConnect(host=self.vcenter_ip, port=self.vcenter_port,
                               user=self.vcenter_user, pwd=self.vcenter_pwd,
                               sslContext=self.context)
        if not self.si:
            print("请检查vc账号密码")
            sys.exit(1)

        self.content = self.si.RetrieveContent()
        atexit.register(Disconnect, self.si)

    # def print_device_info(self, vm):
    #     print("虚拟机设备信息：")
    #     hardware = vm.config.hardware
    #     for device in hardware.device:
    #         print(f"设备类型: {device.__class__.__name__}, 设备名称: {device.deviceInfo.label}")

    # def replace_nic_in_vm(self, vm, old_nic_name, new_network_name):
    #     """
    #     替换虚拟机中的网络适配器。
    #     :param vm: 虚拟机对象
    #     :param old_nic_name: 要替换的旧网络适配器名称
    #     :param new_network_name: 新的网络名称
    #     """
    #     self.print_device_info(vm)
    #
    #     # 确保虚拟机关闭或暂停
    #     if vm.runtime.powerState != vim.VirtualMachine.PowerState.poweredOff:
    #         print(f"虚拟机状态: {vm.runtime.powerState}, 请先关闭虚拟机")
    #         return
    #
    #     # 获取虚拟机的硬件配置
    #     hardware = vm.config.hardware
    #     devices = hardware.device
    #
    #     # 查找旧的网络适配器
    #     old_nic_device = None
    #     print("虚拟机中的所有网络适配器:")
    #     for device in devices:
    #         if isinstance(device, vim.vm.device.VirtualEthernetCard):
    #             print(f"设备名称：{device.deviceInfo.label}")
    #             if device.deviceInfo.label == old_nic_name:
    #                 old_nic_device = device
    #                 break
    #
    #     if not old_nic_device:
    #         raise ValueError(f"未找到网络适配器：{old_nic_name}")
    #
    #     # 查找新的网络
    #     network = self.get_obj(self.content, [vim.Network], new_network_name)
    #     if not network:
    #         raise ValueError(f"未找到网络：{new_network_name}")
    #
    #     # 创建新的网络适配器
    #     new_nic = vim.vm.device.VirtualEthernetCard()
    #     new_nic.deviceInfo = vim.Description()
    #     new_nic.deviceInfo.label = old_nic_name  # 保留原适配器的名称
    #     new_nic.deviceInfo.summary = f"连接到 {new_network_name}"
    #     new_nic.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
    #     new_nic.backing.network = network
    #     new_nic.backing.deviceName = network.name
    #     new_nic.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
    #     new_nic.connectable.connected = True
    #     new_nic.connectable.allowGuestControl = True
    #
    #     # 创建设备变更列表
    #     dev_changes = []
    #
    #     # 移除旧网卡
    #     remove_spec = vim.vm.device.VirtualDeviceSpec()
    #     remove_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove
    #     remove_spec.device = old_nic_device
    #     dev_changes.append(remove_spec)
    #
    #     # 添加新网卡
    #     add_spec = vim.vm.device.VirtualDeviceSpec()
    #     add_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
    #     add_spec.device = new_nic
    #     dev_changes.append(add_spec)
    #
    #     # 更新虚拟机配置
    #     config_spec = vim.vm.ConfigSpec(deviceChange=dev_changes)
    #
    #     try:
    #         task = vm.ReconfigVM_Task(spec=config_spec)
    #         WaitForTask(task)
    #         print(f"网络适配器 {old_nic_name} 已成功替换为 {new_network_name}")
    #     except vim.fault.NotSupported as e:
    #         # 捕获 NotSupported 异常并输出详细信息
    #         print(f"操作不支持：{str(e)}")
    #     except Exception as e:
    #         # 捕获其他类型的异常并打印
    #         print(f"更新网络适配器时发生错误：{str(e)}")

    def vm_clone(self, src_vmname, dest_vmname, cluster_name, datastore_name, newCPU, newMemMB, newIP, newSubMask,
                 newGateWay, newDNS):
        if not src_vmname or not dest_vmname:
            print("源或目标虚拟机设置错误")
            sys.exit(1)

        # 查找源虚拟机对象
        src_vm = self.get_obj(self.content, [vim.VirtualMachine], src_vmname)
        if not src_vm:
            raise SystemExit("源虚拟机未找到")

        # 配置目标存储位置
        datastore = self.get_obj(self.content, [vim.Datastore], datastore_name)
        if not datastore:
            raise SystemExit("存储地址未找到")

        # 查找目标资源池（使用集群的默认资源池）
        cluster = self.get_obj(self.content, [vim.ClusterComputeResource], cluster_name)
        if not cluster:
            raise SystemExit("目标集群未找到")

        resource_pool = cluster.resourcePool
        if not resource_pool:
            raise SystemExit("目标资源池未找到")

        # 配置目标虚拟机的存储和资源池
        relocate_spec = vim.vm.RelocateSpec()
        relocate_spec.datastore = datastore
        relocate_spec.pool = resource_pool

        # 硬件配置
        config_spec = vim.vm.ConfigSpec(
            name=dest_vmname,
            numCPUs=newCPU,
            memoryMB=newMemMB,
            deviceChange=[]  # 添加或修改设备
        )

        # 自定义网络设置
        customization_spec = self.create_customization_spec(
            dest_vmname,
            ip_address=newIP,
            subnet_mask=newSubMask,
            gateway=newGateWay,
            dns=newDNS
        )

        # 克隆配置
        clone_spec = vim.vm.CloneSpec(
            powerOn=False,  # 克隆后不启动
            template=False,  # 克隆后不是模板
            location=relocate_spec,
            config=config_spec,  # 应用CPU内存设置
            customization=customization_spec  # 应用网络设置
        )

        # 执行克隆任务
        print("开始克隆虚拟机...")
        task = src_vm.CloneVM_Task(folder=src_vm.parent, name=dest_vmname, spec=clone_spec)
        WaitForTask(task)
        print("虚拟机克隆完成")
        print("请手动替换网络适配器，检查网络配置")

    @staticmethod
    def get_obj(content, vimtype, name):
        """获取vSphere对象"""
        obj = None
        container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
        for c in container.view:
            if c.name == name:
                obj = c
                break
        container.Destroy()
        return obj

    @staticmethod
    def create_customization_spec(hostname, ip_address, gateway, dns, subnet_mask):
        """创建自定义规格"""
        # 网络适配器设置
        adapter_map = vim.vm.customization.AdapterMapping()
        adapter_map.adapter = vim.vm.customization.IPSettings(
            ip=vim.vm.customization.FixedIp(ipAddress=ip_address),
            subnetMask=subnet_mask,
            gateway=gateway,
            dnsServerList=dns
        )
        # 全局IP设置
        global_ip = vim.vm.customization.GlobalIPSettings(dnsServerList=dns)
        # Linux系统标识
        identity = vim.vm.customization.LinuxPrep(
            domain="local",
            hostName=vim.vm.customization.FixedName(name=hostname)
        )
        # 自定义规格
        customization_spec = vim.vm.customization.Specification()
        customization_spec.nicSettingMap = [adapter_map]
        customization_spec.globalIPSettings = global_ip
        customization_spec.identity = identity
        return customization_spec


if __name__ == '__main__':
    # vCenter信息
    vcenter_ip = "10.2.31.140"
    vcenter_port = "443"
    vcenter_user = "administrator@vsphere.local"
    vcenter_pwd = "*(Ow5AXgf{"

    # 定义要克隆的虚拟机和目标位置
    src_vm_name = 'Centos-MB'
    dest_vm_name = 'snc-test-imp'
    cluster_name = "ClusterYeWu03"
    datastore_name = "XT480_SAS_VS6_VMLUN02"
    newCPU = 4
    newMemGB = 8


    # 自定义IP
    newIP = "10.2.158.65"
    newSubMask = "255.255.255.0"
    newGateWay = ["10.2.158.1"]
    newDNS = ["8.8.8.8"]

    newMemMB = newMemGB * 1024
    vc = vCenterManager(vcenter_ip, vcenter_port, vcenter_user, vcenter_pwd)
    vc.vm_clone(src_vm_name, dest_vm_name, cluster_name, datastore_name, newCPU, newMemMB, newIP, newSubMask,
                newGateWay, newDNS)
